#include "imageitem.h"
#include "math.h"
#include <QDebug>

ImageItem::ImageItem(QGraphicsItem *parent)
    : QGraphicsPixmapItem(parent)
{
    setAcceptDrops(true);
    setAcceptsHoverEvents(true);
    setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsFocusable);

    m_mode = MOVE;
    m_margin = 6;
    m_degree = 0;
    m_tempDegree = 0;
    m_width = 0;
    m_height = 0;

    m_proxy = new ImageItemProxy;

    m_dashRect = NULL;

    createFocusItems();
}

QRectF ImageItem::boundingRect() const
{
    return QRectF(0, 0, m_width, m_height);
}

void ImageItem::setPixmap(const QPixmap &pixmap)
{
    m_width = pixmap.width();
    m_height = pixmap.height();
    QGraphicsPixmapItem::setPixmap(pixmap);
    if (m_mode != TRANSFORM)
    {
        m_originWidth = m_width;
        m_originHeight = m_height;
    }
    if (m_mode != SELECT && m_mode != MOVE)
    {
        return;
    }
    if (m_dashRect)
    {
        QPointF point = m_dashRect->rect().topLeft();
        if (point.x() > m_width || point.y() > m_height ||
            point.x() + m_dashRect->rect().width() > m_width ||
            point.y() + m_dashRect->rect().height() > m_height)
        {
            delete m_dashRect;
            m_dashRect = NULL;
            m_proxy->emitSelectedImage(pixmap.toImage(), QPoint(0, 0));
        }
        else
        {
            updateSelectedImage();
        }
    }
    else
    {
        m_proxy->emitSelectedImage(pixmap.toImage(), QPoint(0, 0));
    }
}

void ImageItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    if (m_mode == TRANSFORM)
    {
        m_curFocusItem = qgraphicsitem_cast<FocusItem*>(scene()->itemAt(event->scenePos()));
        if (m_curFocusItem)
        {
            if (m_curFocusItem->getFocusType() == FocusItem::RESIZE)
            {
                m_originPoint = mapToScene(0, 0);
                m_lastPoint.setX(event->scenePos().x());
                m_lastPoint.setY(event->scenePos().y());
                m_dashRect = new QGraphicsRectItem();
                m_dashRect->setPen(QPen(Qt::DashLine));
                m_dashRect->setParentItem(this);
                m_dashRect->setRect(0, 0, m_width, m_height);
            }
            else
            {
                m_tempDegree = 0;
                m_originPoint = mapToScene(m_width / 2, m_height / 2);
                m_lastPoint.setX(event->scenePos().x());
                m_lastPoint.setY(event->scenePos().y());
                m_dashRect = new QGraphicsRectItem();
                m_dashRect->setPen(QPen(Qt::DashLine));
                m_dashRect->setParentItem(this);
                m_dashRect->setRect(0, 0, m_originWidth, m_originHeight);

                qreal temp = -m_degree * 180 / PI;
                qreal realX = (-m_originWidth / 2) * cos(m_degree) - (m_originHeight / 2) * sin(m_degree);
                qreal realY = (m_originHeight / 2) * cos(m_degree) + (-m_originWidth / 2)* sin(m_degree);
                realX = realX + m_width / 2;
                realY = m_height / 2 - realY;
                m_dashRect->setRotation(temp);
                m_dashRect->setPos(realX, realY);
            }
        }
    }
    else if (m_mode == SELECT)
    {
        m_selected = false;
        if (event->pos().x() < 0 || event->pos().y() < 0)
        {
            m_lastPoint.setX(0);
            m_lastPoint.setY(0);
        }
        else if (event->pos().x() > m_width || event->pos().y() > m_height)
        {
            m_lastPoint.setX(m_width);
            m_lastPoint.setY(m_height);
        }
        else
        {
            m_lastPoint.setX(event->pos().x());
            m_lastPoint.setY(event->pos().y());
        }
    }
    else
    {
        QGraphicsPixmapItem::mousePressEvent(event);
    }
}

void ImageItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    if(m_curFocusItem && m_mode == TRANSFORM)
    {
        if (m_curFocusItem->getFocusType() == FocusItem::RESIZE)
        {
            QPointF curPoint(event->scenePos());

            qreal curX = m_originPoint.x();
            qreal curY = m_originPoint.y();
            qreal curWidth = m_originWidth;
            qreal curHeight = m_originHeight;
            qreal widthChanging = curPoint.x() - m_lastPoint.x();
            qreal heightChanging = curPoint.y() - m_lastPoint.y();

            FocusItem::PosInHost pos = m_curFocusItem->getInHost();
            switch(pos)
            {
            case FocusItem::NORTH_MIDDLE:
                curY += heightChanging;
                curHeight -= heightChanging;
                break;
            case FocusItem::SOUTH_MIDDLE:
                curHeight += heightChanging;
                break;
            case FocusItem::EAST_MIDDLE:
                curWidth += widthChanging;
                break;
            case FocusItem::WEST_MIDDLE:
                curX += widthChanging;
                curWidth -= widthChanging;
                break;
            case FocusItem::NORTH_WEST:
                curX += widthChanging;
                curY += heightChanging;
                curWidth -= widthChanging;
                curHeight -= heightChanging;
                break;
            case FocusItem::SOUTH_EAST:
                curWidth += widthChanging;
                curHeight += heightChanging;
                break;
            case FocusItem::NORTH_EAST:
                curY += heightChanging;
                curWidth += widthChanging;
                curHeight -= heightChanging;
                break;
            case FocusItem::SOUTH_WEST:
                curX += widthChanging;
                curWidth -= widthChanging;
                curHeight += heightChanging;
                break;
            default:
                break;
            }
            if(curWidth < 20 || curHeight < 20)
            {
                return; //!minimal size
            }
            m_proxy->emitSizeChangedSingal(curWidth, curHeight);
            setPos(QPointF(curX, curY));
            m_dashRect->setRect(0, 0, curWidth, curHeight);
            showFocusItems(true);
        }
        else
        {
            QPointF curPoint(event->scenePos());
            qreal originX = m_originPoint.x();
            qreal originY = m_originPoint.y();

            qreal alpha, beta, degree;
            qreal curX = curPoint.x() - originX;
            qreal curY = originY - curPoint.y();
            qreal lastX = m_lastPoint.x() - originX;
            qreal lastY = originY - m_lastPoint.y();

            alpha = asin(lastY / sqrt(lastY * lastY + lastX * lastX));
            beta = asin(curY / sqrt(curY * curY + curX * curX));
            if (lastX < 0)
            {
                alpha = PI - alpha;
            }
            if (curX < 0)
            {
                beta = PI - beta;
            }

            degree = beta - alpha;
            m_tempDegree = degree;
            degree += m_degree;
            m_proxy->emitRotateSignal(degree);

            QPointF curOriginPoint(mapToScene(m_width / 2, m_height / 2));
            qreal xChanged = m_originPoint.x() - curOriginPoint.x();
            qreal yChanged = m_originPoint.y() - curOriginPoint.y();
            QPointF picOrigin(mapToScene(0, 0));
            setPos(QPointF(picOrigin.x() + xChanged, picOrigin.y() + yChanged));

            qreal temp = -degree * 180 / PI;
            qreal realX = (-m_originWidth / 2) * cos(degree) - (m_originHeight / 2) * sin(degree);
            qreal realY = (m_originHeight / 2) * cos(degree) + (-m_originWidth / 2)* sin(degree);
            realX = realX + m_width / 2;
            realY = m_height / 2 - realY;
            m_dashRect->setRotation(temp);
            m_dashRect->setPos(realX, realY);
            showFocusItems(true);
        }
    }
    else if (m_mode == SELECT)
    {
        QPointF curPoint = event->pos();
        if (curPoint.x() < 0)
        {
            curPoint.setX(0);
        }
        if (curPoint.y() < 0)
        {
            curPoint.setY(0);
        }
        if (curPoint.x() > m_width)
        {
            curPoint.setX(m_width);
        }
        if (curPoint.y() > m_height)
        {
            curPoint.setY(m_height);
        }
        qreal originX = curPoint.x() < m_lastPoint.x() ? curPoint.x() : m_lastPoint.x();
        qreal originY = curPoint.y() < m_lastPoint.y() ? curPoint.y() : m_lastPoint.y();

        qreal w = fabs(curPoint.x() - m_lastPoint.x());
        qreal h = fabs(curPoint.y() - m_lastPoint.y());

        if (m_dashRect != NULL)
        {
            delete m_dashRect;
            m_dashRect = NULL;
        }
        m_dashRect = new QGraphicsRectItem();
        m_dashRect->setPen(QPen(Qt::DashLine));
        m_dashRect->setParentItem(this);
        m_dashRect->setRect(originX, originY, w, h);
        m_selected = true;
    }
    else
    {
        QGraphicsPixmapItem::mouseMoveEvent(event);
    }
}

void ImageItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    if(m_curFocusItem && m_mode == TRANSFORM)
    {
        if (m_curFocusItem->getFocusType() == FocusItem::RESIZE)
        {
            m_originWidth = m_width;
            m_originHeight = m_height;
            qreal curX = m_dashRect->rect().left();
            qreal curY = m_dashRect->rect().top();
            QPointF curPos(curX,curY);
            setPos(mapToScene(curPos));
            delete m_dashRect;
            m_dashRect = NULL;
        }
        else
        {
            m_degree += m_tempDegree;
            delete m_dashRect;
            m_dashRect = NULL;
        }
    }
    else if (m_mode == SELECT)
    {
        if (m_selected)
        {
            updateSelectedImage();
        }
        else
        {
            if (m_dashRect != NULL)
            {
                delete m_dashRect;
                m_dashRect = NULL;
            }
        }
        if (!m_dashRect)
        {
            m_proxy->emitSelectedImage(pixmap().toImage(), QPoint(0, 0));
        }
    }
    else
    {
        QGraphicsPixmapItem::mouseReleaseEvent(event);
    }
}

void ImageItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
{
    QPointF point = event->pos();
    int x = point.x();
    int y = point.y();
    QString str = "x=" + QString::number(x) + ", y=" + QString::number(y);
    QImage image = pixmap().toImage();
    QRgb rgb = image.pixel(x, y);
    if (image.allGray())
    {
        str += "; gray value=" + QString::number(qRed(rgb)) + ".";
    }
    else
    {
        str += "; red=" + QString::number(qRed(rgb)) + ", green="
                + QString::number(qGreen(rgb)) + ", blue="
                + QString::number(qBlue(rgb)) + ".";
    }
    m_proxy->emitPointPixelSignal(str);
}

void ImageItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *)
{
    m_proxy->emitHoverLeaveSignal();
}

void ImageItem::hoverEnterEvent(QGraphicsSceneHoverEvent *)
{
    if (m_mode == SELECT)
    {
        setCursor(Qt::CrossCursor);
    }
    else
    {
        setCursor(Qt::ArrowCursor);
    }
}

void ImageItem::createFocusItems()
{
    FocusItem* north_middle = new FocusItem(m_margin, FocusItem::NORTH_MIDDLE,
                                            FocusItem::RESIZE, this);
    m_focusItems.append(north_middle);
    FocusItem* north_east = new FocusItem(m_margin, FocusItem::NORTH_EAST,
                                          FocusItem::RESIZE, this);
    m_focusItems.append(north_east);
    FocusItem* west_middle = new FocusItem(m_margin, FocusItem::WEST_MIDDLE,
                                           FocusItem::RESIZE, this);
    m_focusItems.append(west_middle);
    FocusItem* north_west = new FocusItem(m_margin, FocusItem::NORTH_WEST,
                                          FocusItem::RESIZE, this);
    m_focusItems.append(north_west);
    FocusItem* south_middle = new FocusItem(m_margin, FocusItem::SOUTH_MIDDLE,
                                            FocusItem::RESIZE, this);
    m_focusItems.append(south_middle);
    FocusItem* south_west = new FocusItem(m_margin, FocusItem::SOUTH_WEST,
                                          FocusItem::RESIZE, this);
    m_focusItems.append(south_west);
    FocusItem* east_middle = new FocusItem(m_margin, FocusItem::EAST_MIDDLE,
                                           FocusItem::RESIZE, this);
    m_focusItems.append(east_middle);
    FocusItem* south_east = new FocusItem(m_margin, FocusItem::SOUTH_EAST,
                                          FocusItem::RESIZE, this);
    m_focusItems.append(south_east);
}

void ImageItem::updateSelectedImage()
{
    QImage src = pixmap().toImage();
    QRectF rect = m_dashRect->rect();
    int x = (int)rect.topLeft().x();
    int y = (int)rect.topLeft().y();
    int w = rect.width();
    int h = rect.height();
    QImage dst = QImage(w, h, src.format());
    if (src.format() == QImage::Format_Indexed8)
    {
        for (int i = 0; i < h; i++, y++)
        {
            uchar* srcPtr = src.scanLine(y) + x;
            uchar* dstPtr = dst.scanLine(i);
            for (int j = 0; j < w; j++, srcPtr++, dstPtr++)
            {
                *dstPtr = *srcPtr;
            }
        }
    }
    else
    {
        for (int i = 0; i < h; i++, y++)
        {
            QRgb* srcPtr = (QRgb*)src.scanLine(y) + x;
            QRgb* dstPtr = (QRgb*)dst.scanLine(i);
            for (int j = 0; j < w; j++, srcPtr++, dstPtr++)
            {
                *dstPtr = *srcPtr;
            }
        }
    }
    m_proxy->emitSelectedImage(dst, rect.topLeft().toPoint());
}

void ImageItem::showFocusItems(bool visible)
{
    switch (m_type)
    {
    case RESIZE:
        for(int i = 0; i < m_focusItems.count(); i++)
        {
            m_focusItems.at(i)->setWidth(m_originWidth);
            m_focusItems.at(i)->setHeight(m_originHeight);
            m_focusItems.at(i)->setDegree(0);
            m_focusItems.at(i)->setFocusType(FocusItem::RESIZE);
            m_focusItems.at(i)->locateInHost();
            m_focusItems.at(i)->setVisible(visible);
        }
        break;
    case ROTATE:
        for(int i = 0; i < m_focusItems.count(); i++)
        {
            m_focusItems.at(i)->setWidth(m_originWidth);
            m_focusItems.at(i)->setHeight(m_originHeight);
            m_focusItems.at(i)->setDegree(m_degree + m_tempDegree);
            m_focusItems.at(i)->setFocusType(FocusItem::ROTATE);
            m_focusItems.at(i)->locateInHost();
            m_focusItems.at(i)->setVisible(visible);
        }
        break;
    case FREE:
        for(int i = 0; i < m_focusItems.count(); i++)
        {
            m_focusItems.at(i)->setWidth(m_originWidth);
            m_focusItems.at(i)->setHeight(m_originHeight);
            m_focusItems.at(i)->setDegree(m_degree + m_tempDegree);
            if (i % 2 == 0)
            {
                m_focusItems.at(i)->setFocusType(FocusItem::ROTATE);
            }
            else
            {
                m_focusItems.at(i)->setFocusType(FocusItem::RESIZE);
            }
            m_focusItems.at(i)->locateInHost();
            m_focusItems.at(i)->setVisible(visible);
        }
        break;
    }
}

void ImageItem::setMode(ImageItem::MouseMode mode)
{
    m_mode = mode;
//    if (m_dashRect)
//    {
//        delete m_dashRect;
//        m_dashRect = NULL;
//    }
//    m_proxy->emitSelectedImage(pixmap().toImage(), QPoint(0, 0));
}

void ImageItem::focusOutEvent(QFocusEvent *event)
{
    if (m_mode == TRANSFORM)
    {
        m_degree = 0;
        m_tempDegree = 0;
        m_proxy->emitFocusOutSingal();
    }
    else
    {
        QGraphicsPixmapItem::focusOutEvent(event);
    }
}
